package com.ton.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;


public class CacheUtil {
    private static Logger logger = LoggerFactory.getLogger(CacheUtil.class);
    private static Map<String, Object> cacheMap = new ConcurrentHashMap<String, Object>();
    private static Map<String, Long> cacheExpireTimeMap = new ConcurrentHashMap<String, Long>();
    private static long clearTime = 0;
    private static Object lock = new Object();

    public static void clearCache() {
        long now = System.currentTimeMillis();
        int second = 60000;
        if (now - clearTime < second) {
            return;
        }
        //过期超过20秒，就删除掉缓存出去，防止缓存一直变大
        synchronized (lock) {
            if (now - clearTime < second) {
                return;
            }
            clearTime = now;
            new Thread(() -> {
                for (String key : cacheExpireTimeMap.keySet()) {
                    Long time = cacheExpireTimeMap.get(key);
                    if (time != null && time < now - 1000) {
                        cacheMap.remove(key);
                        cacheExpireTimeMap.remove(key);
                    }
                }
                if (cacheMap.size() > 20000 || cacheExpireTimeMap.size() > 20000) {
                    logger.error("CacheUtil size:" + cacheMap.size() + ", cacheExpireTimeMapSize:" + cacheExpireTimeMap.size());
                }
            }).start();

        }
    }

    /**
     * @param key
     * @param obj
     * @param cacheSecond 缓存秒数，最小10秒
     */
    public static void cacheObject(String key, Object obj, long cacheSecond) {
        if (obj == null) return;
        clearCache();
        cacheMap.put(key, obj);
        if (cacheSecond < 1) {
            cacheSecond = 1;
        }
        cacheExpireTimeMap.put(key, System.currentTimeMillis() + cacheSecond * 1000);
    }

    public static void cacheObject(String key, Object obj) {
        if (obj == null) return;
        cacheMap.put(key, obj);
    }


    public static <T> T queryObject(String key) {
        Long time = cacheExpireTimeMap.get(key);
        long now = System.currentTimeMillis();
        Object value = cacheMap.get(key);
        if (time == null || time - now < 0) {
            return null;
        }

        return (T) value;
    }

    public static <T> T queryObjectWithCache(String key, Callable<T> call, long cacheSecond) {
        T t = queryObject(key);
        if (t == null) {
            synchronized (key.intern()) {
                t = queryObject(key);
                if (t == null) {
                    try {
                        t = call.call();
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                    cacheObject(key, t, cacheSecond);
                }
            }
        }
        return t;
    }

    /**
     * 每间隔cacheSecond 时间，会有一个线程去请求最新数据，其他线程仍然使用旧的cache数据
     */
    public static <T> T queryObjectInternalReuqest(String key, Callable<T> call, long cacheSecond) {
        Object value = cacheMap.get(key);
        Long time = cacheExpireTimeMap.get(key);
        long now = System.currentTimeMillis();
        if (value == null || time == null || time - now < cacheSecond * 1000) {
            synchronized (key.intern()) {
                value = cacheMap.get(key);
                time = cacheExpireTimeMap.get(key);
                if (value == null || time == null || time - now < cacheSecond * 1000) {
                    if (value != null) {
                        cacheObject(key, value, cacheSecond * 2);
                    }
                    try {
                        value = call.call();
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                    cacheObject(key, value, cacheSecond * 2);
                }
            }
        }
        return (T) value;
    }

    /**
     * 缓存数据，如果数据被使用会重新被缓存，请谨慎使用
     *
     * @return
     */
    public static <T> T queryObjectWithCacheLoop(String key, Callable<T> call, long cacheSecond) {
        T t = queryObject(key);
        if (t == null) {
            synchronized (key.intern()) {
                t = queryObject(key);
                if (t == null) {
                    try {
                        t = call.call();
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                    cacheObject(key, t, cacheSecond);
                }
            }
        } else {
            cacheObject(key, t, cacheSecond);
        }
        return t;
    }

    public static String queryString(String key) {
        return (String) queryObject(key);
    }

    public static Integer queryInteger(String key) {
        return (Integer) queryObject(key);
    }

    public Long queryLong(String key) {
        return (Long) queryObject(key);
    }

    public static <T> T removeObject(String key) {
        cacheExpireTimeMap.remove(key);
        return (T) cacheMap.remove(key);
    }

}
